/**
 * 
 */
package com.tomagency.cyberlab.server.exports;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringBufferInputStream;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Formatter;
import java.util.HashMap;
import java.util.List;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.apache.log4j.Logger;

import org.apache.commons.net.PrintCommandListener;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPConnectionClosedException;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPListParseEngine;
import org.apache.commons.net.ftp.FTPReply;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import de.gammadata.basics.modelle.VarObjectDO;
import de.gammadata.business.modelle.BelegDO;
import de.gammadata.business.modelle.BelegPositionDO;
import de.gammadata.gwt.modelle.ResultObject;
import de.gammadata.gwt.modelle.ResultObjectImpl;
import de.gammadata.util.BarcodeUtil;

import com.google.inject.Inject;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
import com.tomagency.cyberlab.server.config.CyberlabConfigProvider;
import com.tomagency.cyberlab.server.exports.BubuHandler.BubuStatus;
import com.tomagency.cyberlab.server.imports.TaopixFotobuchSpec;
import com.tomagency.cyberlab.server.util.LoggingUtil;
import com.tomagency.cyberlab.server.util.ServletHelper;


/**
 * Klasse handelt das Erzeugen von Bubu-xml-Files
 * @author gfr
 *
 */
public class BubuHandlerImpl implements  BubuHandler {

//	private String ftpServerName = "ftp-king.bubu.ch";
//	private String ftpUserName = "ftp_cyberlab";
//	private String ftpPassword = "12CyberLab34";
	
//	private HashMap<String, FotoBuchSpec> productList;

	private String xmlTemplate;

	private XStream xstream = new XStream(new DomDriver());
	
	
	@Inject
	private CyberlabConfigProvider cyberlabConfigProvider;

	private LoggingUtil loggingUtil;
	private Logger logger;
	
	@Inject
	public BubuHandlerImpl(LoggingUtil loggingUtil) {
		this.loggingUtil=loggingUtil;
		logger = loggingUtil.getApiLogger(this.getClass().getName());
	}
	/**
	 * @see com.tomagency.cyberlab.server.exports.BubuHandler#genBubuXmlOrderFile()
	 */
	@Override
	public ResultObject genBubuXmlOrderFile(BelegDO beleg, boolean isTest, long orderId, long jobId) {
		
		
		BubuGenResult result = new BubuGenResult();
		FTPClient ftp=openFtp();
		
		if (ftp==null){
			result.setOk(false);
			result.setMessage("Fehler beim Zugriff/Login auf ftpserver: "+cyberlabConfigProvider.getTaopixConfig().getFtpServerConfig().getFtpServerName());
			
		}
		else{
			result =genBubuXmlString(beleg, isTest, orderId, jobId);
			if (result.isOk()){
				if (checkPdfFilesOnFtp(result, ftp)){

					String xmlFile = result.xmlString;
					if (xmlFile==null||xmlFile.length()<20){
						result.setOk(false);
						result.setMessage("Fehler beim generieren xml-datei");
						
					}
					else{
						
						String remoteFileName = Long.toString(orderId)+".xml";
		
		//				InputStream input = new StringBufferInputStream(xmlFile);				
		         
		                try {
		                	
		                	ByteArrayInputStream input=new ByteArrayInputStream(xmlFile.getBytes("UTF-8"));
		//                	File temp=File.createTempFile(Long.toString(orderId), ".xml");
		//                	boolean ok=ServletHelper.saveTextFile(temp, xmlFile, "UTF-8");
		//                	logger.info("genBubuXmlOrderFile() erzeuge temp-Datei: "+temp.getAbsolutePath());
		//                	FileInputStream input = new FileInputStream(temp);
		                	ftp.setFileType(FTP.BINARY_FILE_TYPE);
							ftp.storeFile(remoteFileName, input);
							input.close();
							result.setMessage("Orderfile auf FTP geschrieben: "+remoteFileName);
							result.setOk(true);
							
						} catch (IOException e) {
							result.setOk(false);
							result.setMessage("Fehler beim Schreiben xml-File auf FTP:  "+e.getMessage());
							logger.error("genBubuXmlOrderFile Ftp-Fehler beim Schreiben Xml-File: ", e);
						}
						
					}
				}
				else{
					result.setOk(false);
					if (result.getFotoBuchSpec()!=null){
						result.setMessage("Zuerst Pdf-dateien auf FTP laden: "
								+result.getFotoBuchSpec().getCoverFileName()
								+", "+result.getFotoBuchSpec().getPagesFileName());
					}
					
				}
			}
		}
		
		
		if (ftp!=null){
			try {
				ftp.logout();
				if (ftp.isConnected())	            
					ftp.disconnect();
			} catch (IOException e) {
				logger.error("genBubuXmlOrderFile Ftp-Fehler beim logout/disconnect: ", e);
			}
		}
		return result;
	}
	
	private boolean checkPdfFilesOnFtp(BubuGenResult result,FTPClient ftp){
		boolean gefunden=false;
		if (result==null||result.getFotoBuchSpec()==null){
			return false;
		}
		
		try {

			FTPListParseEngine engine = ftp.initiateListParsing();
			
			boolean coverFileGefunden=false;
			boolean pageFileGefunden=false;
			
			if (result.getFotoBuchSpec().getCoverFileName()==null){
				coverFileGefunden=true;
			}
			else{
				logger.info("checkPdfFilesOnFtp(): suche nach "+result.getFotoBuchSpec().getCoverFileName());
			}
			if (result.getFotoBuchSpec().getPagesFileName()==null){
				pageFileGefunden=true;
			}
			else{
				logger.info("checkPdfFilesOnFtp(): suche nach "+result.getFotoBuchSpec().getPagesFileName());
			}
			
			gefunden=coverFileGefunden&pageFileGefunden;
			
			while(engine.hasNext()&!gefunden){
        		List<FTPFile> files = Arrays.asList(engine.getNext(25));
//                System.out.println("Verzeichnis Listing "+files.size()+ " Dateien");
                
                for (FTPFile file : files){
                	if (file.isFile()){
                	
                		String name=file.getName();
                		logger.info("checkPdfFilesOnFtp() vergleiche: "+name);
                		if (!coverFileGefunden&&(name.equalsIgnoreCase(result.getFotoBuchSpec().getCoverFileName()))){
                			coverFileGefunden=true;
                			logger.info("checkPdfFilesOnFtp() gefunden: "+result.getFotoBuchSpec().getCoverFileName());
                		}
                		if (!pageFileGefunden&&(name.equalsIgnoreCase(result.getFotoBuchSpec().getPagesFileName()))){
                			pageFileGefunden=true;
                			logger.info("checkPdfFilesOnFtp() gefunden: "+result.getFotoBuchSpec().getPagesFileName());
                		}
                		
                		gefunden=coverFileGefunden&pageFileGefunden;
                		
                		if (gefunden)
                			break;
                		
                	}
                }
			}
		} catch (Exception e) {
			logger.error("checkPdfFilesOnFtp() Fehler: "+e,e);
			return false;
		}
		
		return gefunden;
	}
	
	public class BubuGenResult extends ResultObjectImpl implements ResultObject{
		private TaopixFotobuchSpec fotoBuchSpec;
		private String xmlString;
		
		BubuGenResult(){}
		
		BubuGenResult(TaopixFotobuchSpec fotoBuchSpec,String xmlString){
			this.fotoBuchSpec=fotoBuchSpec;
			this.xmlString=xmlString;
			
		}
		/**
		 * @return the fotoBuchSpec
		 */
		public TaopixFotobuchSpec getFotoBuchSpec() {
			return fotoBuchSpec;
		}
		/**
		 * @param fotoBuchSpec the fotoBuchSpec to set
		 */
		public void setFotoBuchSpec(TaopixFotobuchSpec fotoBuchSpec) {
			this.fotoBuchSpec = fotoBuchSpec;
		}
		/**
		 * @return the xmlString
		 */
		public String getXmlString() {
			return xmlString;
		}
		/**
		 * @param xmlString the xmlString to set
		 */
		public void setXmlString(String xmlString) {
			this.xmlString = xmlString;
		}
	}
	
	
	/**
	 * @see com.tomagency.cyberlab.server.exports.BubuHandler#genBubuXmlFile()
	 */
	public BubuGenResult genBubuXmlString(BelegDO beleg, boolean isTest, long orderId, long jobId) {

		BubuGenResult bResult=new BubuGenResult();
		bResult.setOk(false);
		bResult.setMessage("nicht näher spezifizierter Fehler bei genBubuXmlString()");
		
		if (beleg!=null&&beleg.getPositionsListe()!=null&&beleg.getPositionsListe().size()>0&this.cyberlabConfigProvider.getBubuConfig().getProductList()!=null){
			String besteller= beleg.getKontakt().getNamenString();
			bResult.setMessage("Produkt ist kein Fotobuch");
			for (BelegPositionDO pos: beleg.getPositionsListe()){
				String productCode = pos.getProdukt().getArtikelCode();
				if (this.cyberlabConfigProvider.getBubuConfig().getProductList().containsKey(productCode.toLowerCase())){
					FotoBuchSpec bubuSpec = this.cyberlabConfigProvider.getBubuConfig().getProductList().get(productCode.toLowerCase());
					logger.info("genBubuXmlFile: Produktcode gefunden: "+productCode);
					VarObjectDO spec = pos.getSpecObject();
					if (spec!=null&&spec.getObjectClassName().contains("TaopixFotobuchSpec")){
						Object specObj=xstream.fromXML(spec.getXmlStream());
						logger.info("genBubuXmlFile: SpecObjekt gefunden: "+specObj.getClass().getName());
						if (specObj instanceof TaopixFotobuchSpec){
							try {
								TaopixFotobuchSpec fbSpec = (TaopixFotobuchSpec)specObj;
								
								StringBuilder sb = new StringBuilder();
								Formatter formatter = new Formatter(sb);
								if (jobId>=cyberlabConfigProvider.getBubuConfig().getJobIdStartNummer())
									formatter.format("%012d", jobId);
								else
									formatter.format("%012d", jobId+cyberlabConfigProvider.getBubuConfig().getJobIdStartNummer());
//								String jobIdbarcode = BarcodeUtil.generateEan13String(jobId);
								String jobIdTxt = sb.toString();
								
								sb = new StringBuilder();
								formatter = new Formatter(sb);
								if(orderId>=cyberlabConfigProvider.getBubuConfig().getOrderIdStartNummer())
										formatter.format("%012d", orderId);
								else
									formatter.format("%012d", orderId+cyberlabConfigProvider.getBubuConfig().getOrderIdStartNummer());
								String orderIdTxt = sb.toString();
								
								String text = beleg.getSubject()+", "+pos.getBezeichnung()+", "+besteller;
								
								String test=BubuHandler.IS_TEST;
								if (!isTest)
									test=BubuHandler.IS_PRODUCTION;
								
								String xmlResult = this.cyberlabConfigProvider.getBubuConfig().getXmlTemplate().replaceAll(TEST, test)
								.replaceAll(ORDERID, orderIdTxt)
								.replaceAll(JOBID, jobIdTxt)
								.replaceAll(PRODUCTCODE, bubuSpec.getProductCode())
								.replaceAll(FORMAT, bubuSpec.getFormatCode())
								.replaceAll(QTY, Integer.toString(fbSpec.getQty()))
								.replaceAll(PAGECOUNT, Integer.toString(fbSpec.getPageCount()))
								.replaceAll(COVER_PAPERCODE, bubuSpec.getCoverPaperCode())
								.replaceAll(COATING, bubuSpec.getCoverPaperCoating())
								.replaceAll(CONTENT_PAPERCODE, bubuSpec.getContentPaperCode())								
								.replaceAll(COVER_FILENAME, fbSpec.getCoverFileName())
								.replaceAll(CONTENT_FILENAME, fbSpec.getPagesFileName())
								.replaceAll(TEXT, text)
								.replaceAll(VERMERK, text)
								;
								bResult.setOk(true);
								bResult.setMessage("");
								bResult.setXmlString(xmlResult);
								bResult.setFotoBuchSpec(fbSpec);
								
							} catch (Exception e) {
								bResult.setOk(false);
								bResult.setMessage("Exception beim generieren Fotobuch xml: "+e.getMessage());
								logger.error("genBubuXmlFile: Fehler beim object cast auf TaopixFotobuchSpec oder beim String Replacement im xml-template",e);
							}
						}
						else{
							bResult.setOk(false);
							bResult.setMessage("Produkt ist kein Fotobuch");
						}
					}
					else{
						bResult.setOk(false);
						bResult.setMessage("Produkt ist kein Fotobuch");
					}
				}

			}
			
		}
		else{
			bResult.setOk(false);
			bResult.setMessage("Fehlerhafter Beleg oder Konfiguration");
		}
		return bResult;
	}
	
	@Override
	public BubuXmlParserResultImpl processBubuXmlResponse(String xml) {
		
		BubuXmlParserResultImpl result= new BubuXmlParserResultImpl();
		
		try {
			BubuResponseSaxHandler handler = new BubuResponseSaxHandler();
			getSaxParser().parse(new StringBufferInputStream(xml), handler);
			result.setOk(true);
			result.setStatusList(handler.getStatusList());
			result.setMessage("processBubuXmlResponse parsen ok");
			
		} catch (Exception e) {
			result.setOk(false);
			result.setMessage("processBubuXmlResponse() Fehler beim parsen: "+e.getMessage());
			logger.error("processBubuXmlResponse() Fehler beim parsen: "+e.getMessage(),e);
		}
				
		return result;
	}
	
	SAXParserFactory factory;
	SAXParser saxParser;
	private SAXParser getSaxParser() throws ParserConfigurationException, SAXException{

		if (factory==null)
			factory = SAXParserFactory.newInstance();
		if (saxParser==null)
			saxParser = factory.newSAXParser();

		return saxParser;
	}
	
	private class BubuResponseSaxHandler extends DefaultHandler {


		private List<BubuStatus> statusList;
		/**
		 * @return the statusList
		 */


		private StringBuilder resultTxt;
		private int orderNummer;
		
		
		private StringBuilder charSeq;
		private String jobIdTxt;
		private String jobStatus;
		private String orderIdTxt;
		private String orderStatus;
		
//		public BubuXmlParserResultImpl getBubuXmlParserResult(){
//			
//			BubuXmlParserResultImpl result= new BubuXmlParserResultImpl();
//			
//			result.setOk(true);
//			result.setStatusList(getStatusList());
//			
//			return result;
//			
//		}


		/* (non-Javadoc)
		 * @see org.xml.sax.helpers.DefaultHandler#startDocument()
		 */
		@Override
		public void startDocument() throws SAXException {
			statusList= new ArrayList<BubuStatus>();
		}

		/* (non-Javadoc)
		 * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String, java.lang.String, java.lang.String)
		 */
		@Override
		public void endElement(String uri, String localName, String qName)
				throws SAXException {
			
			if (qName.equalsIgnoreCase(BubuStatus.JOBID)){
				jobIdTxt=charSeq.toString();
			}
			else if (qName.equalsIgnoreCase(BubuStatus.JOBSTATUS)){
				jobStatus=charSeq.toString();
			}
			else if (qName.equalsIgnoreCase(BubuStatus.ORDERID)){
				orderIdTxt=charSeq.toString();
			}
			else if (qName.equalsIgnoreCase(BubuStatus.ORDERSTATUS)){
				orderStatus=charSeq.toString();
			}
			else if (qName.equalsIgnoreCase("item")){
				logger.info("bubu response jobid="+jobIdTxt+", jobstatus="+jobStatus+", orderid="+orderIdTxt+", orderstatus="+orderStatus);
				long orderNr = Long.parseLong(orderIdTxt)-cyberlabConfigProvider.getBubuConfig().getOrderIdStartNummer();
				orderNummer = (int)orderNr;
				if (resultTxt.length()>0)
					resultTxt.append(", ");
				resultTxt.append(BubuHandler.BUBU_MSG_SUBJECT+"Order "+orderIdTxt+"="+orderStatus+", Job "+jobIdTxt+"="+jobStatus);
				
				statusList.add(new BubuStatusImpl(orderNummer,orderStatus,jobStatus));
			}
		}


		/* (non-Javadoc)
		 * @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes)
		 */
		@Override
		public void startElement(String uri, String localName, String qName,
				Attributes attributes) throws SAXException {
			charSeq = new StringBuilder();
			if (qName.equalsIgnoreCase("item")){
				jobIdTxt=null;
				jobStatus=null;
				orderIdTxt=null;
				orderStatus=null;
				resultTxt=new StringBuilder();
				
							
			}
		}
		/* (non-Javadoc)
		 * @see org.xml.sax.helpers.DefaultHandler#characters(char[], int, int)
		 */
		@Override
		public void characters(char[] ch, int start, int length)
				throws SAXException {
			
		    {
				String str = new String(ch, start, length);
				charSeq.append(str);
				
		    }
//			super.characters(ch, start, length);
		}

		/* (non-Javadoc)
		 * @see org.xml.sax.helpers.DefaultHandler#endDocument()
		 */
		@Override
		public void endDocument() throws SAXException {
			// TODO Auto-generated method stub
//			super.endDocument();
		}
		

		
		public List<BubuStatus> getStatusList() {
			return statusList;
		}
//
//
//		/**
//		 * @param statusList the statusList to set
//		 */
//		public void setStatusList(HashMap statusList) {
//			this.statusList = statusList;
//		}


		
	}

	 private FTPClient openFtp(){
		 FTPClient ftp= new FTPClient();
		 try {
			 ftp.setAutodetectUTF8(true);
			ftp.connect(cyberlabConfigProvider.getBubuConfig().getFtpConfig().getFtpServerName());
			 int reply = ftp.getReplyCode();			 
			 if (!FTPReply.isPositiveCompletion(reply)){
			  ftp.disconnect();
		        logger.error("openFtp(): Fehler  kann nicht mit Ftp-Server verbinden: "+cyberlabConfigProvider.getTaopixConfig().getFtpServerConfig().getFtpServerName());
		        return null; 
		     }
			 if (!ftp.login(cyberlabConfigProvider.getBubuConfig().getFtpConfig().getFtpUserName(),
					 cyberlabConfigProvider.getBubuConfig().getFtpConfig().getFtpPassword()))
	            {
	                ftp.logout();
	                ftp.disconnect();
			        logger.error("doTheScan(): Fehler  kann nicht mit Ftp-Server verbinden, Login fehlgeschlagen: "+cyberlabConfigProvider.getBubuConfig().getFtpConfig().getFtpServerName());
	                return null; 
	            }
			 
			 return ftp;
		 }
		 catch (Exception e) {
			logger.error("openFtp(): Fehler ",e);

		}
		 return null;
		 
	 }


	public CyberlabConfigProvider getCyberlabConfigProvider() {

		return cyberlabConfigProvider;
	}


	public void setCyberlabConfigProvider(
			CyberlabConfigProvider cyberlabConfigProvider) {
		this.cyberlabConfigProvider=cyberlabConfigProvider;
		
	}
	
	private class BubuStatusImpl implements BubuStatus{

		private int belegNummer;
		private String jobStatus;
		private String orderStatus;
		
		public BubuStatusImpl(){
			
		}
		
		public BubuStatusImpl(int orderNummer, String orderStatus, String jobStatus){
			this.belegNummer=orderNummer;
			this.orderStatus=orderStatus;
			this.jobStatus=jobStatus;
			
		}

		@Override
		public int getBelegNummer() {
			return belegNummer;
		}

		@Override
		public String getJobStatus() {
			return jobStatus;
		}

		@Override
		public String getOrderStatus() {
			return orderStatus;
		}

		@Override
		public void setBelegNummer(int orderNummer) {
			this.belegNummer=orderNummer;
			
		}

		@Override
		public void setJobStatus(String jobStatus) {
			this.jobStatus=jobStatus;
			
		}

		@Override
		public void setOrderStatus(String orderStatus) {
			this.orderStatus=orderStatus;
			
		}
		
	}



}
